//==========================================================================
// Copyright (c) 2000-2008,  Elastos, Inc.  All Rights Reserved.
//==========================================================================
#include <ddk.h>
#include <drvpub.h>

#define MAX_POSTERN_CELL 100
#define MAX_CMD_LINE_LEN 128
#define ARG_CMD        0
#define ARG_EXT_CMD    1
#define ARG_INVALID    2
#define ARG_EXIT       3


static DzMutex g_PosternLock;
static DzEvent g_PosternEvent;
static int g_CurCellNum = 0;
static int g_CurCmdLen = 0;
static IThread *g_pPosternThd = NULL;
static PosternFunc g_PostFunc[MAX_POSTERN_CELL];


EXTERN_C int __cdecl DBG_RecvChar();
EXTERN_C void __cdecl DBG_SendChar(unsigned char c);
EXTERN_C void watchdog_xllp_fire(void);
EXTERN_C int watchdog_init(void);
            
long strtohex(char *str)
{
    long tmp = 0, s1;
    char *p = str;
    bool sig = false;

    while ( (*p != '\0') && (*p != '\n') && (*p != '\r') ) {
        s1 = *p;
        if (sig) {
            if ( (s1 >= '0') && (s1 <= '9') ) {
                s1 = s1 - '0';
            }
            else if ( (s1 >= 'a') && (s1 <= 'f') ) {
                s1 = s1 - 'a' + 10;
            }
            else if ( (s1 >= 'A') && (s1 <= 'F') ) {
                s1 = s1 - 'A' + 10;
            }
            else {
                return -1;
            }
            tmp = (tmp << 4) + s1;
        }
        else if ( (s1 == 'x') || (s1 == 'X') ) {
            sig = true;
        }
        p++;
    }

    return tmp;
}

long strtodec(char *str)
{
    long tmp = 0, s1;
    char *p = str;
    bool sig = false;

    while ( (*p != '\0') && (*p != '\n') && (*p != '\r') ) {
        s1 = *p;
        if ( (s1 >= '0') && (s1 <= '9') ) {
            s1 = s1 - '0';
            sig = true;
        }
        else if (s1 == ' '){
            if (sig) {
                return -1;
            }
        }
        else {
            return -1;
        }
        tmp = (tmp * 10) + s1;
        p++;
    }

    return tmp;
}

void ShowTips()
{
    int i;
    kputs("\n\r**************************************************\n");
    kputs("\r1. Bind Specify Device!\n \
           \r2. Dump GPIO Registers Value!\n \
           \r3. Read Customer Registers Value!\n \
           \r4. Edit Customer Registers Value!\n \
           \r5. Call Chat\n \
           \r6. Trace\n \
           \r7. Watchdog fire!\n \
           \r8. Watchdog Init!\n \
           \r9. Exit Postern\n");
    kputs("\n\r--------------------------------------------------\n");
    kprintf("\rThere is %2d Externl Functions:\n", g_CurCellNum);

    for (i = 0; i < g_CurCellNum; i++) {
        kprintf("\rExt%2d. %s\n \
                 \r\t Sample %s\n",
                 i,
                 g_PostFunc[i].CmdDescriptor,
                 g_PostFunc[i].CmdSample);
    }
    kputs("--------------------------------------------------\n");
    kputs("\rUsage:\n \
           \rEnter Number <1> to select basic 1 function\n \
           \rEnter <Ext1> to select External 1 function\n");

    kputs("\n\r**************************************************\n");
}


EXTERN_C Boolean __cdecl AddPosternCell( FuncCell func,
                                      int argc,
                                      Byte **argv,
                                      char* CmdDescriptor,
                                      char* CmdSample )
{
    Boolean ret;
    WaitResult wr;
    g_PosternLock.Lock(&wr);
    if (MAX_POSTERN_CELL > g_CurCellNum) {
        g_PostFunc[g_CurCellNum].PosternCell = func;
        g_PostFunc[g_CurCellNum].Argc = argc;
        g_PostFunc[g_CurCellNum].Argv = argv;
        g_PostFunc[g_CurCellNum].CmdDescriptor = CmdDescriptor;
        g_PostFunc[g_CurCellNum].CmdSample = CmdSample;
        g_CurCellNum++;
        ret = TRUE;
    }
    else {
        ret = FALSE;
    }
    g_PosternLock.Unlock();
    return ret;
}

char SendCmdChar(char c)
{
    DBG_SendChar((unsigned char)c);
    return c;
}

char GetCmdChar()
{
    int c;
    while ( (c = DBG_RecvChar()) < 0 );
    SendCmdChar((char)c);
    //kprintf("<%02x>", c);
    return (char)c;
}



UInt32 __cdecl GetCmdStr(char* CmdStrBuf)
{
    char Ch;
    int idx = 0;
    UInt32 cst = ARG_CMD;
    for(;;) {
        Ch = GetCmdChar();
        if ( 0x08 == Ch ) {
            if (idx > 0) {
                idx--;
                CmdStrBuf[idx] = '\0';
                SendCmdChar(' ');
                SendCmdChar((char)0x08);
            }
        }
        else {
            if (MAX_CMD_LINE_LEN <= idx) {
                cst = ARG_INVALID;
                break;
            }

            if ( ('\n' == Ch) || ('\r' == Ch) ) {
                break;
            }
            CmdStrBuf[idx++] = Ch;
        }
    }

    g_CurCmdLen = idx;
    if (idx == 0) {
        cst = ARG_INVALID;
    }

    if (ARG_CMD == cst) {
        if (idx > 3) {//eg:Ext1
            if (
                ( (CmdStrBuf[0] == 'E') || (CmdStrBuf[0] == 'e') ) &&
                ( (CmdStrBuf[1] == 'X') || (CmdStrBuf[1] == 'x') ) &&
                ( (CmdStrBuf[2] == 'T') || (CmdStrBuf[2] == 't') )
               )
            {
                cst = ARG_EXT_CMD;
            }
        }
    }
    return cst;
}

EXTERN_C UInt32 save_flags_cli();
EXTERN_C void restore_flags(UInt32);
EXTERN_C void  DumpGpio();
EXTERN_C Boolean  EditMemory(long offset, long *valst, long lstlen);
EXTERN_C void  ViewMemory(long offset, long length);
EXTERN_C ECode CallServiceFunc(wchar_t *SerName, int OpCode);

Boolean __cdecl PstEditMemory()
{
    long offset, value;
    char CmdOp[MAX_CMD_LINE_LEN], Ch;
    kputs("Enter The Memory Address. eg : 0x80001000\n");
    memset(CmdOp, 0, MAX_CMD_LINE_LEN);
    GetCmdStr(CmdOp);
    offset = strtohex(CmdOp);
    if (-1 == offset) {
        kputs("InValid Offset!\n");
        return FALSE;
    }
    kputs("Enter The Value. eg: 0x0001\n");
    memset(CmdOp, 0, MAX_CMD_LINE_LEN);
    GetCmdStr(CmdOp);
    value = strtohex(CmdOp);
    kprintf("YOU WANT TO CHANGE THE MEMORY[%08x] VALUE TO [%08x]? Y/N:\n",
            offset, value);
    Ch = GetCmdChar();
    if ( ('Y' == Ch) || ('y' == Ch) ) {
        return EditMemory(offset, &value, 1);
    }
    return FALSE;
}

void  __cdecl PstViewMemory()
{
    long offset, range;
    char CmdOp[MAX_CMD_LINE_LEN];
    kputs("Enter The Memory Address. eg : 0x80001000\n");
    memset(CmdOp, 0, MAX_CMD_LINE_LEN);
    GetCmdStr(CmdOp);
    offset = strtohex(CmdOp);
    if (-1 == offset) {
        kputs("InValid Offset!\n");
        return;
    }

    kputs("Enter The Range. eg: 512\n");
    memset(CmdOp, 0, MAX_CMD_LINE_LEN);
    GetCmdStr(CmdOp);
    range = strtodec(CmdOp);
    if (-1 == range) {
        kputs("Out Of Range!\n");
        return;
    }
    kprintf("Dump Memory Form %08x Range %d\n", offset, range);
    ViewMemory(offset, range);
}

ECode __cdecl PstCallServiceFunc()
{
    char CmdOp[MAX_CMD_LINE_LEN], Ch;
    wchar_t ServName[33];
    int OpCode, i, j = 0;
    ECode ec = NOERROR;
    kputs("Enter The Service Or Device's Name. eg : device:display0\n");
    memset(CmdOp, 0, MAX_CMD_LINE_LEN);
    GetCmdStr(CmdOp);
    for(i = 0; (i < MAX_CMD_LINE_LEN) && (j < 32); i++) {
        if(' ' == CmdOp[i]) {
            if (j) {
                ec = E_INVALID_ARGUMENT;
                goto __Exit;
            }
        }
        else {
            if (('\n' != CmdOp[i]) && ('\r' != CmdOp[i]) && ('\0' != CmdOp[i])) {
                ServName[j] = (wchar_t)CmdOp[i];
                j++;
            }
            else {
                break;
            }
        }
    }

    if ( ( j > 0 ) && (j < 32) ) {
        ServName[j] = L'\0';
    }
    else {
        kputs("Invalid Service Name!\n");
        goto __Exit;
    }
    kputs("Enter The Function's Op Code eg: 1\n");
    memset(CmdOp, 0, MAX_CMD_LINE_LEN);
    GetCmdStr(CmdOp);
    OpCode = (int)strtodec(CmdOp);
    kprintf("YOU WANT TO CALL THE %S's CONTROL METHOD WITH OPCODE %d? Y/N:\n", \
            ServName , OpCode);
    Ch = GetCmdChar();
    if ( ('Y' == Ch) || ('y' == Ch) ) {
        ec = CallServiceFunc(ServName, OpCode);
    }
__Exit:
    return ec;
}

UInt32 __cdecl BasicCellEntry(int fCode, Byte *CmdStr)
{
    UInt32 cst = ARG_CMD;
    UInt32 flags;

    switch(fCode) {
        case 1://Bind Specify Device
            PstCallServiceFunc();
            break;

        case 2://Dump GPIO
            DumpGpio();
            break;

        case 3://Read Customer Registers
            PstViewMemory();
            break;

        case 4://Edit Customer Registers
            PstEditMemory();
            break;

        case 5://Call Chat
            flags = save_flags_cli();
            Chat();
            restore_flags(flags);
            break;

        case 6://Trace a variable
            break;

        case 7://Watchdog Fire
            watchdog_xllp_fire();
            break;
            
        case 8://Init Watchdog
            watchdog_init();
            break;
            
        case 9://Exit Postern
            cst = ARG_EXIT;
            break;

        default:
            kputs("Unknown Basic Function Code!\n");
            break;
    }
    return cst;
}

UInt32 __cdecl ExternalCellEntry(int fCode, Byte *CmdStr)
{
    UInt32 cst = ARG_EXT_CMD;
    if ( (fCode >= g_CurCellNum) || (fCode == -1) ) {
        kputs("External Cell Function Out of boundary\n");
        cst = ARG_INVALID;
    }
    else {
        kprintf("Call External Function : %s\n",
                 g_PostFunc[fCode].CmdDescriptor);
        g_PostFunc[fCode].PosternCell( g_PostFunc[fCode].Argc,
                                       g_PostFunc[fCode].Argv );
    }
    return cst;
}

UInt32 __cdecl EntryPosternCell(UInt32 CmdSt, char* CmdStr)
{
    int fCode = 0, offset = 0;;
    UInt32 cst = CmdSt;

    if (ARG_CMD == CmdSt) {
        kputs("Entry Basic Postern Cell\n");
    }
    else if (ARG_EXT_CMD == CmdSt) {
        offset = 3;
        kputs("Entry External Postern Cell\n");
    }
    else {
        goto __Exit;
    }

    if (g_CurCmdLen >= (offset + 1)) {
        if( (CmdStr[offset] >= '0') && (CmdStr[offset] <= '9') ) {
            fCode = CmdStr[offset] - '0';
        }

        if (g_CurCmdLen > (offset + 1)) {
            if( (CmdStr[offset + 1] >= '0') && (CmdStr[offset + 1] <= '9') ) {
                fCode = fCode * 10 + CmdStr[offset + 1] - '0';
            }
        }

        if (g_CurCmdLen > (offset + 2)) {
            if( (CmdStr[offset + 2] >= '0') && (CmdStr[offset + 2] <= '9') ) {
                fCode = -1;//exceed 99
            }
        }
    }
    else {
        cst = ARG_INVALID;
        goto __Exit;
    }

    kprintf("Entry %s Postern Cell %d\n", (offset)?"External":"Basic", fCode);
    if (ARG_CMD == CmdSt) {
        cst = BasicCellEntry(fCode, (Byte*)CmdStr);
    }
    else if (ARG_EXT_CMD == CmdSt) {
        cst = ExternalCellEntry(fCode, (Byte*)CmdStr);
    }
__Exit:
    return cst;
}

EXTERN_C void __cdecl PosternNotify()
{
    g_PosternEvent.Notify ();
}

EXTERN_C void __cdecl PosternEntry()
{
    char CmdStr[MAX_CMD_LINE_LEN];
    UInt32 CmdStatus = 0;
    do {
        memset(CmdStr, 0, MAX_CMD_LINE_LEN);
        ShowTips();
        CmdStatus = GetCmdStr(CmdStr);
        if (ARG_INVALID > CmdStatus) {
            CmdStatus = EntryPosternCell(CmdStatus, CmdStr);
        }
    }while(ARG_EXIT != CmdStatus);
}

EXTERN_C ECode __cdecl PosternThread(void* argv)
{
//    char CmdStr[MAX_CMD_LINE_LEN];
    WaitResult wr;
    while(1) {
        g_PosternEvent.Wait(&wr, NULL);
        PosternEntry();
//        do {
//            memset(CmdStr, 0, MAX_CMD_LINE_LEN);
//            ShowTips();
//            CmdStatus = GetCmdStr(CmdStr);
//            if (ARG_INVALID > CmdStatus) {
//                CmdStatus = EntryPosternCell(CmdStatus, CmdStr);
//            }
//        }while(ARG_EXIT != CmdStatus);
    }
    return NOERROR;
}

ECode CreatePosternThread()
{
    return DzCreateKernelThread( PosternThread,
                                 NULL, CreateThreadFlag_System,
                                 ThreadPriorityRank_Normal, &g_pPosternThd);
}

EXTERN_C ECode __cdecl InitPostern()
{
    ECode ec = NOERROR;
    if (NULL == g_pPosternThd) {
        ec = CreatePosternThread();
        if (FAILED(ec)) {
            kputs("Create Postern Thread Failed!\n");
        }
        else {
            kputs("Create Postern Thread Succeeded!\n");
        }
    }
    return NOERROR;
}

